///////////////////////////////////////////////////////////////////////////////////////////////////
/// \file formic/vector/vector.cpp
///
/// \brief   implementation file for the formic::Vector class
///
///////////////////////////////////////////////////////////////////////////////////////////////////

#include<complex>

#include<formic/vector/vector.h>
#include<formic/mpi/interface.h>
#include<formic/output/output.h>

//template<class S, class L> std::list<L>                       formic::VectorLock<S,L>::available_lengths;
//template<class S, class L> std::list<boost::shared_array<S> > formic::VectorLock<S,L>::available_arrays;
template<class S, class L> std::map<L, std::stack<boost::shared_array<S> > > formic::VectorLock<S,L>::available_arrays;

template<class S, class L> formic::VectorLock<S,L>::VectorLock(const L n) {

  // set the length
  _length = n;

  if ( n > 0 ) {

    this->pthread_lock();

//          assert( available_lengths.size() == available_arrays.size() );
//          typename std::list<L>::iterator                       li = available_lengths.begin();
//          typename std::list<boost::shared_array<S> >::iterator ai = available_arrays.begin();
//
//          // try to find an already-allocated array
//          bool found = false;
//          for ( ; ai != available_arrays.end(); ai++, li++) {
//            if ( *li == n ) {
//              _length = *li;
//              _array = *ai;
//              available_lengths.erase(li);
//              available_arrays.erase(ai);
//              found = true;
//              break;
//            }
//          }

    // get the array stack for this length
    std::stack<boost::shared_array<S> > & st = available_arrays[n];

    // if there is an array of the correct length available, use it
    if ( st.size() > 0 ) {

      //if ( formic::mpi::rank() == 0 )
      //  formic::of << boost::format("using a stacked array of size %i") % _length << std::endl;
      _array = st.top();
      st.pop();

    // otherwise, allocate a new array
    } else {

      //if ( formic::mpi::rank() == 0 )
      //  formic::of << boost::format("allocating new array of size %i") % n << std::endl;
      boost::shared_array<S> temp_array( new S[n] );
      _array = temp_array;

    }

    this->pthread_unlock();

    //// fill the array as specified
    //std::fill(_array.get(), _array.get() + n, val);
    ////for (L i = 0; i < n; i++)
    ////  _array[i] = val;

  }

}

template formic::VectorLock<int,                     int>::VectorLock(const int n);
template formic::VectorLock<double,                  int>::VectorLock(const int n);
template formic::VectorLock<std::complex<double>,    int>::VectorLock(const int n);
template formic::VectorLock<int,                  size_t>::VectorLock(const size_t n);
template formic::VectorLock<double,               size_t>::VectorLock(const size_t n);
template formic::VectorLock<std::complex<double>, size_t>::VectorLock(const size_t n);

template<class S, class L> formic::VectorLock<S,L>::~VectorLock() {
  this->pthread_lock();
  if (_length > 0) {
    //if ( formic::mpi::rank() == 0 )
    //  formic::of << boost::format("stacking an array of size %i") % _length << std::endl;
    //available_lengths.push_back(_length);
    //available_arrays.push_back(_array);
    available_arrays[_length].push(_array);
  }
  _array.reset();
  this->pthread_unlock();
}

template formic::VectorLock<int,                     int>::~VectorLock();
template formic::VectorLock<double,                  int>::~VectorLock();
template formic::VectorLock<std::complex<double>,    int>::~VectorLock();
template formic::VectorLock<int,                  size_t>::~VectorLock();
template formic::VectorLock<double,               size_t>::~VectorLock();
template formic::VectorLock<std::complex<double>, size_t>::~VectorLock();

template<class S, class L> void formic::VectorLock<S,L>::garbage_collect() {
  VectorLock<S,L>::pthread_lock();
  //for (typename std::list<L>::const_iterator li = available_lengths.begin();
  //     li != available_lengths.end(); li++)
  //  formic::of << boost::format("deallocating an array of size %i") % *li << std::endl;
  //available_lengths.clear();
  //if ( formic::mpi::rank() == 0 )
  //  for (typename std::map<L, std::stack<boost::shared_array<S> > >::iterator mi = available_arrays.begin(); mi != available_arrays.end(); mi++)
  //    formic::of << boost::format("deallocating %i arrays of size %i") % mi->second.size() % mi->first << std::endl;
  available_arrays.clear();
  VectorLock<S,L>::pthread_unlock();
}

template void formic::VectorLock<int,                     int>::garbage_collect();
template void formic::VectorLock<double,                  int>::garbage_collect();
template void formic::VectorLock<std::complex<double>,    int>::garbage_collect();
template void formic::VectorLock<int,                  size_t>::garbage_collect();
template void formic::VectorLock<double,               size_t>::garbage_collect();
template void formic::VectorLock<std::complex<double>, size_t>::garbage_collect();

void formic::vector_garbage_collect() {
  formic::VectorLock<int,                     int>::garbage_collect();
  formic::VectorLock<double,                  int>::garbage_collect();
  formic::VectorLock<std::complex<double>,    int>::garbage_collect();
  formic::VectorLock<int,                  size_t>::garbage_collect();
  formic::VectorLock<double,               size_t>::garbage_collect();
  formic::VectorLock<std::complex<double>, size_t>::garbage_collect();
}

//static void formic_vector_function_to_force_instantiations() {
//
//  { formic::Vector<                 int, int> v(2); }
//  { formic::Vector<              double, int> v(2); }
//  { formic::Vector<std::complex<double>, int> v(2); }
//
//  { formic::Vector<                 int, size_t> v(2); }
//  { formic::Vector<              double, size_t> v(2); }
//  { formic::Vector<std::complex<double>, size_t> v(2); }
//
////  formic::Vector<S,L> vec1(2);
////  formic::Vector<S,L> vec2(vec1);
////  formic::Vector<S,L> vec3(vec1);
////  vec3 = vec1 + vec2;
////  vec3 = vec1 - vec2;
////  vec3 = vec1 * vec2;
////  vec3 = vec1 / vec2;
//
//}
